// ----------------------------------------------------------------------------
// serialize_allocator.cpp
// ----------------------------------------------------------------------------
/*
 Description:
 .
 */
#include <fw/base/serialize/serialize_allocator.h>
#include <fw/base/serialize/serialize_type.h>
#include <fw/base/core/assertion.h>

namespace fw
{
	SerializeAllocator::SerializeAllocator()
	{
		SetBuffer(nullptr, 0);
	}
	
	SerializeAllocator::SerializeAllocator(char* data, size_t size)
	{
		SetBuffer(data, size);
	}
	
	void SerializeAllocator::SetBuffer(char *data, size_t size)
	{
		m_data			= data;
		m_size			= size;
		m_offset		= 0;
		m_blockCount	= 0;
		m_pointerCount	= 0;
	}
	
	char* SerializeAllocator::Alloc(const SerializeType*	dstType,
									const SerializeType*	srcType,
									const char*				srcData,
									int						count)
	{
		char* p = nullptr;
		
		Block* block = FindBlock(dstType, srcType, srcData, count);
		if(!block)
		{
			block = AddBlock(dstType, srcType, srcData, count);
		}
		if(m_data && m_offset<=m_size)
		{
			p = m_data + block->m_offset;
		}
		
		return p;
	}
	
	void SerializeAllocator::StorePointerLocation(const char *data)
	{
		m_pointers[m_pointerCount] = data;
		m_pointerCount++;
	}
	
	int SerializeAllocator::GetPointerLocationCount() const
	{
		return m_pointerCount;
	}
	
	const char* SerializeAllocator::GetPointerLocation(int index) const
	{
		FW_ASSERT(index<kMaxEntries);
		return m_pointers[index];
	}
	
	SerializeAllocator::Block* SerializeAllocator::AddBlock(const SerializeType* dstType,
															const SerializeType* srcType,
															const char*			 srcData,
															int					 count)
	{
		FW_ASSERT(dstType);
		FW_ASSERT(srcType);
		FW_ASSERT(srcData);
		
		Block* block = m_blocks + m_blockCount;
		m_blockCount++;
		
		block->m_dstType = dstType;
		block->m_srcType = srcType;
		block->m_srcData = srcData;
		block->m_count	 = count;
		
		m_offset += (m_offset) & (dstType->GetAlignment() - 1);
		block->m_offset = m_offset;
		
		m_offset += dstType->GetSize() * count;
		
		return block;
	}
	
	SerializeAllocator::Block* SerializeAllocator::FindBlock(const SerializeType* dstType,
															 const SerializeType* srcType,
															 const char*		  srcData,
															 int                  count)
	{
		FW_ASSERT(dstType);
		FW_ASSERT(srcType);
		FW_ASSERT(srcData);
		for(int blk_i=0; blk_i<m_blockCount; ++blk_i)
		{
			Block* block = &m_blocks[blk_i];
			if(block->m_dstType == dstType
			&& block->m_srcType == srcType
			&& block->m_srcData == srcData
			&& block->m_count   == count)
			{
				return block;
			}
		}
		return nullptr;
	}
	
	void SerializeAllocator::ConvertAll()
	{
		for(int blk_i=0; blk_i<GetCount(); ++blk_i)
		{
			const Block* block = GetBlock(blk_i);
			ConvertBlock(block);
		}
	}
	
	SerializeStatus SerializeAllocator::ConvertBlock(const Block* block)
	{
		FW_ASSERT(block);
		
		char*       dstData = GetDstPtr(block);
		const char* srcData = block->m_srcData;
		
		FW_ASSERT(srcData);
		
		size_t dstStride = dstData ? block->m_dstType->GetSize() : 0;
		size_t srcStride =           block->m_srcType->GetSize();
		
		for(int blk_i=0; blk_i<block->m_count; ++blk_i)
		{
			block->m_dstType->ConvertOne(dstData, srcData, block->m_srcType, *this, 1);
			dstData += dstStride;
			srcData += srcStride;
		}
		
		return kStatus_Ok;
	}
	
	char* SerializeAllocator::GetDstPtr(const Block *block) const
	{
		if(!m_data)
		{
			return nullptr;
		}
		if(m_offset>m_size)
		{
			return nullptr;
		}
		return &m_data[block->m_offset];
	}
	
} // namespace fw
